﻿//-----------------------------------------------------------------------
// <copyright file="TupleLinkedList.cs" company="tuliprules.org">
//     Copyright (c) 2010 tuplelister, tuliprules.org.
// </copyright>
// <summary>This is the TupleLinkedList class.</summary>
//-----------------------------------------------------------------------

namespace Tulip.Engine.Tuples
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;

    /// <summary>
    /// A linked list of tuples.
    /// </summary>
    [Serializable]
    [DebuggerDisplay("Count = {Count}")]
    public class TupleLinkedList
    {
        /// <summary>
        /// Returns the current Tuple at the head of the linked list.
        /// </summary>
        public Tuple StartTuple;

        /// <summary>
        /// Returns the current Tuple at the end of the linked list.
        /// </summary>
        public Tuple EndTuple;

        /// <summary>
        /// Unique identifier for the tuple linked list.   Used by tuples to
        /// select the correct pointers for next and previous tuples.
        /// </summary>
        public int IndexerId;

        /// <summary>
        /// Unique identifier for the tuple relation that stores this linked list.
        /// Used to differentiate between linked lists when same tuple is stored
        /// in more than one memory.
        /// </summary>
        public int TupleRelationId;

        /// <summary>
        /// Indicates if this tuple linked list instance is currently pooled.
        /// </summary>
        public bool IsPooled;

        /// <summary>
        /// Nested tuple view, used for compound indexes.
        /// </summary>
        public IDictionary NestedTupleView;

        /// <summary>
        /// The current list mode.
        /// </summary>
        /// <remarks>
        /// All -         The enumerator will enumerate all Tuples in the list.
        /// Gen0Only -    The enumerator will enumerate only the Generation 0 
        ///               Tuples in the list.
        /// NotGet0  -    The enumerator will enumerate a;ll tuples in the list 
        ///               except the Generation 0 tuples.
        /// </remarks>
        public TupleLinkedListMode Mode = TupleLinkedListMode.All;

        /// <summary>
        /// Count of tuples in the list.
        /// </summary>
        private int count;

        /// <summary>
        /// Count of all tuples in the list.
        /// </summary>
        private int countAll;

        /// <summary>
        /// Count of generation 0 tuples in the list.
        /// </summary>
        private int countGen0Only;

        /// <summary>
        /// Count of non-generation 0 tuples in the list.
        /// </summary>
        private int countNotGen0;

        /// <summary>
        /// Index number of the tuple list.
        /// </summary>
        private int index;

        /// <summary>
        /// Current tuple.
        /// </summary>
        private Tuple current;

        /// <summary>
        /// Gets the number of elements actually contained in the Tulip.Engine.Tuples.TupleLinkedList.
        /// </summary>
        /// <value>
        /// The number of elements actually contained in the Tulip.Engine.Tuples.TupleLinkedList.
        /// </value>
        public int Count
        {
            get
            {
                switch (this.Mode)
                {
                    case TupleLinkedListMode.All:
                        this.count = this.countAll;
                        break;
                    case TupleLinkedListMode.Gen0Only:
                        this.count = this.countGen0Only;
                        break;
                    case TupleLinkedListMode.NotGen0:
                        this.count = this.countNotGen0;
                        break;
                }

                return this.count;
            }
        }

        /// <summary>
        /// Returns a new 'View' dictionary for TupleLinkedList.
        /// </summary>
        /// <typeparam name="T">The type of the key value.</typeparam>
        /// <returns>A tuple view.</returns>
        public static IDictionary GetTupleView<T>()
        {
            return new Dictionary<T, TupleLinkedList>();
        }

        /// <summary>
        /// Clears the current TupleLinkedList.
        /// </summary>
        public void Clear()
        {
            this.IndexerId = 0;
            this.StartTuple = default(Tuple);
            this.EndTuple = default(Tuple);
            this.count = 0;
            this.countAll = 0;
            this.countGen0Only = 0;
            this.countNotGen0 = 0;
            this.index = 0;
            this.current = default(Tuple);
            this.Mode = TupleLinkedListMode.All;
            this.NestedTupleView = default(IDictionary);
        }

        /// <summary>
        /// Adds an object to the head of the Tulip.Engine.Tuples.TupleLinkedList.
        /// </summary>
        /// <param name="tuple">
        /// The tuple to be added to the head of the Tulip.Engine.Tuples.TupleLinkedList.
        /// </param>
        public void Add(Tuple tuple)
        {
            if (tuple == null)
            {
                return;
            }

            tuple.Link(this);
            this.EndTuple = tuple;

            if (this.StartTuple == null)
            {
                this.StartTuple = tuple;
            }

            this.countAll++;

            if (tuple.Generation == 0)
            {
                this.countGen0Only++;
            }
            else
            {
                this.countNotGen0++;
            }

            switch (this.Mode)
            {
                case TupleLinkedListMode.All:
                    this.count = this.countAll;
                    break;
                case TupleLinkedListMode.Gen0Only:
                    this.count = this.countGen0Only;
                    break;
                case TupleLinkedListMode.NotGen0:
                    this.count = this.countNotGen0;
                    break;
            }
        }

        /// <summary>
        /// <para>Advances the enumerator to the next element of the Tulip.Engine.Tuples.TupleLinkedList.</para>
        /// <para>Returns:
        ///     true if the enumerator was successfully advanced to the next element; false
        ///     if the enumerator has passed the end of the collection.</para>
        /// <para>Exceptions:
        ///   System.InvalidOperationException:
        ///     The collection was modified after the enumerator was created.</para>
        /// </summary>
        /// <returns>
        /// true if the enumerator was successfully advanced to the next element; 
        /// false  if the enumerator has passed the end of the collection.
        /// </returns>
        public Tuple Next()
        {
            if (this.StartTuple == null)
            {
                // reset
                this.current = default(Tuple);
                this.index = 0;
                return default(Tuple);
            }

            switch (this.Mode)
            {
                case TupleLinkedListMode.Gen0Only:

                    if (this.current == default(Tuple))
                    {
                        this.index = 0;
                        this.current = this.StartTuple;
                    }

                    while (this.current.Generation != 0)
                    {
                        if (this.index >= this.count)
                        {
                            this.current = default(Tuple);
                            this.index = 0;
                            return default(Tuple);
                        }

                        this.current = this.current.GetNextTuple(this);
                    }

                    this.index++;
                    return this.current;

                case TupleLinkedListMode.NotGen0:
                    if (this.current == default(Tuple))
                    {
                        this.index = 0;
                        this.current = this.StartTuple;
                    }

                    while (this.current.Generation == 0)
                    {
                        if (this.index >= this.count)
                        {
                            this.current = default(Tuple);
                            this.index = 0;
                            return default(Tuple);
                        }

                        this.current = this.current.GetNextTuple(this);
                    }

                    this.index++;
                    return this.current;

                default:
                    if (this.current == default(Tuple))
                    {
                        this.current = this.StartTuple;
                        this.index++;
                        return this.current;
                    }

                    if (this.index < this.count)
                    {
                        this.current = this.current.GetNextTuple(this);
                        this.index++;
                        return this.current;
                    }

                    this.current = default(Tuple);
                    this.index = 0;
                    return default(Tuple);
            }
        }

        /// <summary>
        ///  Removes the first occurrence of a specific object from the System.Collections.Generic.List&lt;T&gt;.
        /// </summary>
        /// <param name="item">
        /// The object to remove from the System.Collections.Generic.List&lt;T&gt;. The value
        /// can be null for reference types.
        /// </param>
        public void Remove(Tuple item)
        {
            this.UnlinkTuple(item);
        }

        /// <summary>
        /// Unlinks a tuple from the tuple list and returns the tuple.
        /// </summary>
        /// <param name="tuple">The tuple to be unlinked.</param>
        private void UnlinkTuple(Tuple tuple)
        {
            try
            {
                if (tuple == this.EndTuple)
                {
                    this.EndTuple = tuple.GetPreviousTuple(this.IndexerId);
                }

                if (tuple == this.StartTuple)
                {
                    this.StartTuple = null;
                }

                tuple.Unlink(this);
                this.countAll--;

                if (tuple.Generation == 0)
                {
                    this.countGen0Only--;
                }
                else
                {
                    this.countNotGen0--;
                }

                switch (this.Mode)
                {
                    case TupleLinkedListMode.All:
                        this.count = this.countAll;
                        break;
                    case TupleLinkedListMode.Gen0Only:
                        this.count = this.countGen0Only;
                        break;
                    case TupleLinkedListMode.NotGen0:
                        this.count = this.countNotGen0;
                        break;
                }

                return;
            }
            catch (Exception)
            {
                return;
            }
        }
    }
}
